package main

import (
	"fmt"
	_ "youbei/boot"

	"io/ioutil"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"youbei/icon"
	"youbei/task"

	"gitee.com/johng/gf/g/encoding/gjson"
	"gitee.com/johng/gf/g/os/gflock"
	"gitee.com/johng/gf/g/os/glog"
	"github.com/getlantern/systray"
	"github.com/sciter-sdk/go-sciter"
	"github.com/sciter-sdk/go-sciter/window"
	"github.com/skratchdot/open-golang/open"
)

const (
	server       = "http://idcyw.cn"
	serverstatic = "http://idcyw.cn"
)

var rootfile = GetCurrentDirectory()

/*
	系统托盘图标退出函数处理
*/
func onExit() {
	task.Endtask()
	os.Exit(0)
}

/*
	系统托盘图标准备函数处理
*/
func onReady() {

	// We can manipulate the systray in other goroutines
	go func() {
		systray.SetIcon(icon.Data)
		systray.SetTitle("柚备数据安全")
		systray.SetTooltip("柚备数据安全")
		main_ui := systray.AddMenuItem("柚备数据安全", "yb")

		mUrl := systray.AddMenuItem("柚备官网", server)
		slog := systray.AddMenuItem("查看日志", "logs")
		rootpath := systray.AddMenuItem("柚备根目录", "rootpath")
		systray.AddSeparator()
		mQuit := systray.AddMenuItem("退出柚备", "Quit the program")

		for {
			select {
			case <-main_ui.ClickedCh:
				go newwindow()
			case <-mUrl.ClickedCh:
				open.Run(server)
			case <-rootpath.ClickedCh:
				open.Run(rootfile)
			//			case <-mToggle.ClickedCh:
			//				if shown {
			//					mUrl.Hide()
			//					shown = false
			//				} else {
			//					mUrl.Show()
			//					shown = true
			//				}
			case <-slog.ClickedCh:
				open.Run(rootfile + "/logs")
			case <-mQuit.ClickedCh:
				systray.Quit()
				return
			}
		}
	}()
}

/*
	定义事件处理函数
*/
func setEventHandler(w *window.Window) {
	/*
		客户端获取任务列表
	*/
	//name string, st, rep int, ivl string, dbs string
	w.DefineFunction("GetTasklist", func(args ...*sciter.Value) *sciter.Value {
		tlist := task.Tasklist()
		timeLayout := "2006-01-02 15:04:05"
		result := sciter.NewValue()
		if len(tlist) <= 0 {
			result.SetString("暂无数据")
			return result
		}
		for _, tl := range tlist {

			res := sciter.NewValue()
			vmap, _ := gjson.DecodeToJson(tl)

			for k1, v1 := range vmap.ToMap() {
				if k1 == "databases" {
					dbs := ""
					for k2, v2 := range v1.([]interface{}) {
						dbs += v2.(string)
						if k2 != (len(v1.([]interface{})) - 1) {
							dbs += ","
						}
					}
					res.Set(k1, sciter.NewValue(dbs))
				} else {
					res.Set(k1, sciter.NewValue(v1))
				}
				if k1 == "starttime" || k1 == "intime" {

					res.Set(k1, sciter.NewValue(time.Unix(int64(v1.(float64)), 0).Format(timeLayout)))
				}
			}
			result.Append(res)
		}
		return result
	})
	/*
		订购页面跳转
	*/
	w.DefineFunction("booking", func(args ...*sciter.Value) *sciter.Value {
		open.Run(server)
		return sciter.NullValue()
	})
	/*
		客户端添加任务处理
	*/
	w.DefineFunction("addtask", func(args ...*sciter.Value) *sciter.Value {

		usermap := task.GetLocalUser()
		resp := sciter.NewValue()
		var err error
		var res bool
		if usermap == nil {

			resp.Set("success", false)
			resp.Set("msg", "未获取到当前登陆用户信息")
			return resp
		}
		if args[0].IsObject() {
			res, err = task.NewTask(args[0].Get("name").String(), int64(args[0].Get("starttime").Int()), args[0].Get("repeatcount").Int(), args[0].Get("interval").String(), args[0].Get("dbs").String(), usermap["uuid"].(string), args[0].Get("category").String(), args[0].Get("version").String())
		} else {
			res, err = task.NewTask(args[0].String(), args[1].Int64(), args[2].Int(), args[3].String(), args[4].String(), usermap["uuid"].(string), args[5].String(), args[6].String())
		}
		resp.Set("success", res)
		if err != nil {
			resp.Set("msg", err.Error())
		}

		return resp
	})
	/*
		客户端删除任务
	*/
	w.DefineFunction("deletetask", func(args ...*sciter.Value) *sciter.Value {
		resp := sciter.NewValue()
		res, err := task.Deletetask(args[0].String())
		resp.Set("success", res)
		if err != nil {
			resp.Set("msg", err.Error())
		}

		return resp

	})
	/*
		客户端调试控制台输出
	*/
	w.DefineFunction("dump", func(args ...*sciter.Value) *sciter.Value {

		for _, v := range args {
			if v.IsObject() {
				v.EnumerateKeyValue(func(k1, v1 *sciter.Value) bool {
					fmt.Println(k1, v1)

					return true
				})
			} else {

				fmt.Print(v.String() + " ")
			}
		}

		return sciter.NullValue()
	})
	/*
		客户端登录页面跳转
	*/
	w.DefineFunction("login_index", func(args ...*sciter.Value) *sciter.Value {
		w.LoadFile("view/login.html")
		return sciter.NullValue()
	})
	/*
		切换账户
	*/
	w.DefineFunction("logout", func(args ...*sciter.Value) *sciter.Value {
		Logout()
		return sciter.NullValue()
	})
	/*
		修改密码
	*/
	w.DefineFunction("changepwd", func(args ...*sciter.Value) *sciter.Value {
		open.Run(server)
		return sciter.NullValue()
	})
	/*
		退出客户端
	*/
	w.DefineFunction("onexit", func(args ...*sciter.Value) *sciter.Value {
		onExit()
		return sciter.NullValue()
	})
	/*
		客户端用户登录处理函数
	*/
	w.DefineFunction("login", func(args ...*sciter.Value) *sciter.Value {
		resp := Login(args[0].String(), args[1].String())
		result := sciter.NewValue()
		if val, ok := resp.Result.(map[string]interface{}); ok {
			for k, v := range val {
				result.Set(k, sciter.NewValue(v))
			}
		}

		res := sciter.NewValue()
		res.Set("code", sciter.NewValue(resp.Code))
		res.Set("result", result)
		res.Set("msg", sciter.NewValue(resp.Msg))

		return res
	})
	/*
		客户端注册页面跳转
	*/
	w.DefineFunction("register_index", func(args ...*sciter.Value) *sciter.Value {
		open.Run(server + "/home/member/register")
		return sciter.NullValue()
	})
	/*
		注册
	*/
	w.DefineFunction("register", func(args ...*sciter.Value) *sciter.Value {
		r, e := Register(args[0].String(), args[1].String(), args[2].String())
		resp := sciter.NewValue()

		if e != nil {
			resp.Set("success", false)
			resp.Set("msg", e.Error())
			return resp

		}
		if r.Code != 0 {
			resp.Set("success", false)
			resp.Set("msg", r.Msg)
			return resp
		}

		resp.Set("success", true)

		return resp
	})
	/*
		客户端rsa秘钥对生成
	*/
	w.DefineFunction("generatersa", func(args ...*sciter.Value) *sciter.Value {
		resp := sciter.NewValue()
		if AnyKeyisexist() {
			resp.Set("success", false)
			resp.Set("msg", "已存在秘钥对")
			return resp
		}
		err, keyfilemap := GenRsaKey(1024)
		if err == nil {

		} else {
			resp.Set("success", false)
			resp.Set("msg", err.Error())
			return resp
		}

		if Uploadkey(keyfilemap["pri"], keyfilemap["pub"]) {
			resp.Set("success", true)
			return resp
		} else {
			resp.Set("success", false)
			resp.Set("msg", "秘钥对存储失败,请重新生成！")
			return resp
		}

	})
	/*
		客户端检查用户是否登录
	*/
	w.DefineFunction("islogin", func(args ...*sciter.Value) *sciter.Value {
		loginbool := sciter.NewValue()
		usermap := task.GetLocalUser()
		if usermap != nil {
			resp := Login(usermap["username"].(string), usermap["password"].(string))
			if resp.Code == 0 {
				loginbool.SetBool(true)
			} else {
				loginbool.SetBool(false)
			}
		}
		return loginbool
	})
	/*
		客户端上传秘钥事件处理
		filename = rand(uuid)+_(pri|pub).pem
	*/
	w.DefineFunction("uploadkey", func(args ...*sciter.Value) *sciter.Value {
		resp := sciter.NewValue()
		_, err := Keyisexist(args[0].String())
		if err != nil {
			resp.Set("success", false)
			resp.Set("msg", err.Error())
			return resp
		} else {
			res := Uploadkey(args[0].String())
			resp.Set("success", res)
			if !res {
				resp.Set("msg", "上传失败")
			}
			return resp
		}

	})
	/*
		客户端下载秘钥事件处理
		key  = private | public
	*/
	w.DefineFunction("downloadkey", func(args ...*sciter.Value) *sciter.Value {
		resp := sciter.NewValue()
		if args[1].String() == "" {
			resp.Set("success", false)
			resp.Set("msg", "未获取到文件下载路径")
			return resp
		}
		res, err := Downloadkey(args[0].String(), args[1].String())
		resp.Set("success", res)
		if err != nil {
			resp.Set("msg", err.Error())
		}

		return resp
	})
	/*
		客户端删除密钥处理
	*/
	w.DefineFunction("removekey", func(args ...*sciter.Value) *sciter.Value {
		resp := sciter.NewValue()
		if args[0].String() == "" {
			resp.Set("success", false)
			resp.Set("msg", "未获取到要删除的秘钥类型")
			return resp
		}

		res, err := Removekey(args[0].String())
		resp.Set("success", res)
		if err != nil {
			resp.Set("msg", err.Error())
		}

		return resp
	})
	/*
		查看 数据库(mysql)是否配置
	*/
	w.DefineFunction("sqlisconfig", func(args ...*sciter.Value) *sciter.Value {
		return sciter.NewValue(Sqlisconfig())
	})
	/*
		获取数据库配置记录列表
	*/
	w.DefineFunction("sqlconfiglist", func(args ...*sciter.Value) *sciter.Value {
		list := SqlConfiglist()

		result := sciter.NewValue()
		if len(list) > 0 {
			for _, v := range list {
				fmt.Println(string(v))
				res := sciter.NewValue()
				vmap, err := gjson.DecodeToJson(v)
				if err != nil {
					glog.Cat("error").Error(err)
				}
				for k1, v1 := range vmap.ToMap() {
					res.Set(k1, v1)
				}
				result.Append(res)
			}
			return result
		} else {
			return sciter.NewValue("")
		}

	})

	/*
		配置数据库(mysql)列表信息
	*/
	w.DefineFunction("savesqlconfig", func(args ...*sciter.Value) *sciter.Value {

		config := make(map[string]interface{})
		res := sciter.NewValue()
		if args[0].IsObject() {
			config["dbuser"] = args[0].Get("dbuser").String()
			config["dbpass"] = args[0].Get("dbpass").String()
			config["dbserver"] = args[0].Get("dbserver").String()
			config["dbport"] = args[0].Get("dbport").String()
			config["category"] = args[0].Get("category").String()
			config["version"] = args[0].Get("version").String()
		} else {
			res.Set("success", 2)
			res.Set("msg", "未获取到数据库配置参数")
			return res
		}

		ok, err := Savesqlconfig(config)

		res.Set("success", ok)
		if err != nil {
			if strings.Contains(err.Error(), "No connection could be made because the target machine actively refused it") {
				res.Set("msg", "未检测到数据库服务,请确保服务或端口开启")
			} else if strings.Contains(err.Error(), "(using password: YES)") {
				res.Set("msg", "数据库账号或密码错误")
			} else {
				res.Set("msg", err.Error())
			}

		}

		return res

	})
	/*
		获取用户信息
	*/
	w.DefineFunction("getuserinfo", func(args ...*sciter.Value) *sciter.Value {
		usermap := task.GetLocalUser()
		fmt.Println(usermap)
		fmt.Printf("usermap%v\n", usermap)
		result := sciter.NewValue()
		if usermap != nil {
			for k, v := range usermap {
				result.Set(k, sciter.NewValue(v))
			}
		}

		return result
	})

	/*
		配置数据库(mysql)列表信息
	*/
	w.DefineFunction("getsqllist", func(args ...*sciter.Value) *sciter.Value {
		dblist, err := Getsqlist(args[0].String(), args[1].String())
		resp := sciter.NewValue()
		if err != nil {
			resp.Set("success", false)
			if strings.Contains(err.Error(), "No connection could be made because the target machine actively refused it") {
				resp.Set("msg", "未检测到数据库服务,请确保服务或端口开启")
			} else if strings.Contains(err.Error(), "(using password: YES)") {
				resp.Set("msg", "数据库账号或密码错误")
			} else {
				resp.Set("msg", err.Error())
			}
			return resp
		}
		if len(dblist) > 0 {
			dbliststr := strings.Join(dblist, ",")
			resp.Set("success", true)
			resp.Set("msg", dbliststr)
			return resp
		} else {
			resp.Set("success", false)
			resp.Set("msg", "未获取到数据库，请刷新重试")
			return resp
		}

	})
	/*
		获取RSA秘钥对信息
	*/
	w.DefineFunction("getrsainfo", func(args ...*sciter.Value) *sciter.Value {
		rsalist := Getrsainfo()

		result := sciter.NewValue()
		if len(rsalist) > 0 {
			for _, v := range rsalist {
				res := sciter.NewValue()
				vmap, err := gjson.DecodeToJson(v)
				if err != nil {
					glog.Cat("error").Error(err)
				}
				for k1, v1 := range vmap.ToMap() {
					res.Set(k1, v1)
				}
				result.Append(res)
			}

		}
		return result
	})

	/*
	 备份数据库列表展示
	*/
	w.DefineFunction("getdatabaselist", func(args ...*sciter.Value) *sciter.Value {
		resp := Getdatabaselist()
		result := sciter.NewValue()

		if resp.Code == 0 {
			if resp.Result != nil {
				if len(resp.Result.([]interface{})) > 0 {

					for _, v := range resp.Result.([]interface{}) {
						res := sciter.NewValue()
						for k1, v1 := range v.(map[string]interface{}) {
							res.Set(k1, sciter.NewValue(v1))
						}
						result.Append(res)
					}

					return result
				}
			}
		}
		return sciter.NewValue("")
	})

	/*
		备份数据库文件列表展示
	*/
	w.DefineFunction("getdatafilelist", func(args ...*sciter.Value) *sciter.Value {
		resp := Getdatafilelist(args[0].Int())
		result := sciter.NewValue()
		if resp.Code == 0 {
			if resp.Result != nil {
				if len(resp.Result.([]interface{})) > 0 {
					for _, v := range resp.Result.([]interface{}) {
						res := sciter.NewValue()
						for k1, v1 := range v.(map[string]interface{}) {
							res.Set(k1, sciter.NewValue(v1))
						}
						result.Append(res)
					}
					return result
				}
			}
		}
		return sciter.NewValue("")
	})

	/*
		下载FS文件还原备份文件
	*/
	w.DefineFunction("downloadfsfile", func(args ...*sciter.Value) *sciter.Value {

		file := make(map[string]string)
		resp := sciter.NewValue()

		if args[0].IsObject() {

			file["sourcemd5"] = args[0].Get("sourcemd5").String()
			file["sourcefile"] = args[0].Get("sourcefile").String()
			file["sourcekeyfile"] = args[0].Get("sourcekeyfile").String()
			file["filedir"] = args[0].Get("filedir").String()
			file["fileserver"] = args[0].Get("fileserver").String()
			file["filename"] = args[0].Get("filename").String()
			res, err := Downloadfsfile(file, args[1].String())
			resp.Set("success", res)
			if err != nil {
				resp.Set("msg", err.Error())
			}
			return resp
		}

		resp.Set("success", false)
		resp.Set("msg", "参数不正确")
		return resp
	})

}

/*
	定义回调处理，钩子函数
*/
func setCallbackHandlers(w *window.Window) {
	h := &sciter.CallbackHandler{
		OnDataLoaded: func(ld *sciter.ScnDataLoaded) int {
			fmt.Println("loaded", ld.Uri())
			return sciter.LOAD_OK
		},
		OnLoadData: func(ld *sciter.ScnLoadData) int {
			fmt.Println("loading", ld.Uri())
			return sciter.LOAD_OK
		},
		OnEngineDestroyed: func() int {
			fmt.Println("window destroyed")
			W = nil
			return sciter.LOAD_OK
		},
		OnAttachBehavior: func(args *sciter.ScnAttachBehavior) int {
			fmt.Println("attach被调用了")
			return sciter.LOAD_OK
		},
	}

	w.SetCallback(h)
}

/*
	定义元素处理
*/
func setElementHandlers(root *sciter.Element) {

	//	regbtn, err := root.SelectById("#register")
	//	if err != nil {
	//		fmt.Println(err)
	//	}
	//	regbtn.OnClick(func() {

	//	})
	//	root.DefineMethod("mcall", func(args ...*sciter.Value) *sciter.Value {
	//		print("->method args:")
	//		for _, arg := range args {
	//			print(arg.String(), "\t")
	//		}
	//		println()
	//		return sciter.NullValue()
	//	})
}

/*
	主程序入口函数
*/
func main() {

	//创建window窗口
	//参数一表示创建窗口的样式
	//SW_TITLEBAR 顶层窗口，有标题栏
	//SW_RESIZEABLE 可调整大小
	//SW_CONTROLS 有最小/最大按钮
	//SW_MAIN 应用程序主窗口，关闭后其他所有窗口也会关闭
	//SW_ENABLE_DEBUG 可以调试
	//参数二表示创建窗口的矩形
	os.TempDir()
	l := gflock.New("youbei.lock")
	l.Lock()
	go task.GetHostUuid()
	go systray.Run(onReady, onExit)
	go signalhandle()
	go func() {
		time.Sleep(180 * time.Second) //程序启动三分钟后开始检索未执行完成的任务并执行。
		usermap := task.GetLocalUser()
		if usermap != nil && usermap["logout"].(string) == "0" { //0,登录,1,注销如果logout值不是注销，不自动启动任务
			resp := Login(usermap["username"].(string), usermap["password"].(string))
			if resp.Code == 0 {
				task.Startask(usermap["uuid"].(string))
			} else {
				return
			}
		}
	}()
	newwindow()
	l.UnLock()
}

/*
	新建界面窗口
*/
var W interface{}

func newwindow() {
	usermap := task.GetLocalUser()

	var rootfilepath string = rootfile + "/view/index.html"
	if usermap != nil && usermap["logout"].(string) == "0" {

		rootfilepath = rootfile + "/view/index.html"
	} else { //是注销，需要重新登录
		rootfilepath = rootfile + "/view/login.html"
	}

	if W != nil {

		return
	}
	W, _ = window.New(
		sciter.SW_CONTROLS,
		//给窗口设置大小
		&sciter.Rect{Left: 0, Top: 0, Right: 672, Bottom: 517})

	w := W.(*window.Window)
	//加载文件

	//设置标题
	root, err := w.GetRootElement()
	if err != nil {
		fmt.Println("get root element failed: %s", err.Error())
	}
	// set handlers
	setEventHandler(w)
	setElementHandlers(root)
	setCallbackHandlers(w)
	w.LoadFile(rootfilepath)
	//显示窗口
	w.Show()
	//运行窗口，进入消息循环
	w.Run()
}

/*
	信号处理
*/
func signalhandle() {
	c := make(chan os.Signal, 1)
	//捕捉到信号结束任务并退出程序
	signal.Notify(c, syscall.SIGABRT, syscall.SIGTERM, syscall.SIGINT)
	for {
		select {
		case s := <-c:
			task.Endtask()
			ioutil.WriteFile(fmt.Sprintf(`on_exitlog_%s.txt`, time.Now().Format("20060102150405")), []byte("程序捕捉到异常退出信号: "+s.String()+" ,任务终止并推出,退出时间: "+time.Now().Format("2006年01月02号15点04时05分")+";"), 0644)

			os.Exit(0)

		}
	}

}

/*
	获取当前路径
*/
func GetCurrentDirectory() string {
	dir, err := filepath.Abs(filepath.Dir(os.Args[0])) //返回绝对路径  filepath.Dir(os.Args[0])去除最后一个元素的路径
	if err != nil {
		fmt.Println(err)
	}
	return strings.Replace(dir, "\\", "/", -1) //将\替换成/
}
